#include "serialthread.h"
#include <QDate>
#include <QTime>
#include <QDebug>
#include <qsettings.h>
#include <qdir.h>

CSerialThread::CSerialThread()
{
        stopp_b = true;
        write_b = false;
        read_b = false;
        m_HexOutPut =  false;
        m_hexBytes = 0;
	m_cmdlist = NULL;
	m_curCmd = NULL;
}

void CSerialThread::readSettings()
{
    QSettings settings;

    baudrate=settings.value("/serialsetting/Baud", BAUD115200).toInt();
    dataBits=settings.value("/serialsetting/Databits", 8).toInt();
    parity=settings.value("/serialsetting/Parity", "None").toString();
    stop=settings.value("/serialsetting/Stopbits", "1").toString();
    softwareHandshake=settings.value("/serialsetting/SoftwareHandshake", false).toBool();
    hardwareHandshake=settings.value("/serialsetting/HardwareHandshake", false).toBool();
    //dontapplysetting =  settings.value("/serialsetting/DontApplySettings", false).toBool();
    enableloging = settings.value("/serialsetting/EnableLogging",false).toBool();

    m_name = settings.value("/serialsetting/CurrentDevice", "/dev/ttyS0").toString();
    m_writeAble = settings.value("/serialsetting/OpenForWriting", true).toBool();
    m_HexOutPut =  settings.value("/serialsetting/HexOutput", false).toBool();
    m_DatePrefix = settings.value("/serialsetting/DatePrefix", false).toBool();
    m_TimePrefix = settings.value("/serialsetting/TimePrefix", true).toBool();
    m_PrintUnchar = settings.value("/serialsetting/UncharDataPrint", false).toBool();


    logPath = settings.value("/serialsetting/LogFileName", QDir::homePath()+"/serial.log").toString();
    logType = settings.value("/serialsetting/AppendToLogFile", 0).toInt();

    //m_appendReturnWhenSend = settings.value("/serialsetting/AppendReturnWhenSend").toBool();
}

void CSerialThread::run()
{
   // this->setObjectName("SerialThread");
    bool open_result = false;
    readSettings();
    m_errorinfo.clear();

    if(m_SerialPort.isOpen())
    {
        m_SerialPort.close();
    }
    m_SerialPort.setQueryMode(QextSerialBase::Polling);
    m_SerialPort.setPortName(m_name);

    if ( m_writeAble)
    {
         open_result = m_SerialPort.open(QIODevice::ReadWrite);
    }
    else
    {
         open_result = m_SerialPort.open(QIODevice::ReadOnly);
    }
    if(open_result == false)
        return;

    //must be called after (open serial port)
    setNewOptions();
    //m_SerialPort.flush();
    m_SerialPort.setTimeout(SERIAL_READ_GAP);
    m_SerialPort.reset();
    enableLogging(enableloging,logPath,logType);

    while(false == stopp_b)
    {
        if(read_b)
        {
            readData();

            msleep(SERIAL_READ_GAP);
            TrigerCmd(TIME_ELAPSE);
        }
        if(write_b)
        {
            if(writeByteArray.size())
                m_SerialPort.write(writeByteArray);
            write_b = false;
            TrigerCmd(AFTER_WRITE);
        }
    }
    //m_SerialPort.flush();
    m_SerialPort.close();
    enableLogging(false,logPath,logType);
}

bool CSerialThread::TrigerCmd(Triger tg, QString * pstr)
{
    bool nextCmd = false;
	if(NULL == m_curCmd)
		return false;
	else if(LOOP_SUB_CMD == m_curCmd->node.type)
		m_curCmd = m_curCmd->GetNext();

	
	if(tg == TRIGER_TRY_START)
	{
		nextCmd = true;
	}
    else if((m_curCmd->node.data.CmdData.type == WAIT_TIME)&&(tg == TIME_ELAPSE))
    {
        m_curCmd->node.data.CmdData.param.count--;
        if(m_curCmd->node.data.CmdData.param.count <= 0)
        {
            m_curCmd = m_curCmd->GetNext();
            if(m_curCmd)
                nextCmd = true;
        }
    }
    else if((WAIT_READ == m_curCmd->node.data.CmdData.type)&&(tg == NEW_READ_STRING))
    {
         if(-1 != pstr->indexOf(*(m_curCmd->node.data.CmdData.param.str)))
         {
             m_curCmd = m_curCmd->GetNext();
             if(m_curCmd)
                 nextCmd = true;
         }
    }
    else if((WAIT_WRITE_GAP <= m_curCmd->node.data.CmdData.type)&&(tg == AFTER_WRITE))
    {
        if(m_curCmd->node.data.CmdData.param.str->toAscii() == writeByteArray)//write by me
        {
            m_curCmd = m_curCmd->GetNext();
            if(m_curCmd)
                nextCmd = true;;
        }
    }

    if(nextCmd && (m_curCmd->node.data.CmdData.type >= WAIT_WRITE_GAP) && (write_b == false))
    {//
        QString str = *(m_curCmd->node.data.CmdData.param.str);
        writeByteArray.clear();

        if (m_curCmd->node.data.CmdData.type == WRITE_STR)
        {
            writeByteArray = str.toAscii();
        }
        else if(m_curCmd->node.data.CmdData.type == WRITE_HEX)
        {
            char byte;

            while(str.size())
            {
                if(str.at(0) <= '9')
                    byte = str.at(0).toAscii() - '0';
                else
                    byte = str.at(0).toAscii() - 'a' + 10;

                str.remove(0,1);

                if(str.size())
                {
                    if(str.at(0) <= '9')
                        byte = (byte<<4) | (str.at(0).toAscii() - '0');
                    else
                        byte = (byte<<4) | (str.at(0).toAscii() - 'a' + 10);

                    str.remove(0,1);
                }

                writeByteArray += byte;
            }
        }
        else if(m_curCmd->node.data.CmdData.type == WRITE_FILE)
        {
            //assert(0);
           //
        }
        write_b = true;
    }

	
	if((nextCmd)||(NULL == m_curCmd))
                emit ShowExeCmd(m_curCmd);

	
    return true;
}

/*
isHex means to judge str is hex input or string input
when isHex is true, str must be lower case string from '0' - '9' or 'a' - 'f'
*/
bool CSerialThread::writeDataAsynchro(QString str, bool isHex)
{
    int wtIdx = 0;

    if(stopp_b)
        return false;

    while(write_b && (wtIdx <20))
    {
        wait(100);
        wtIdx ++;
    }
    if(false ==  write_b)
    {
        if(isHex)
        {
            char byte;
            writeByteArray.clear();
            while(str.size())
            {
                if(str.at(0) <= '9')
                    byte = str.at(0).toAscii() - '0';
                else
                    byte = str.at(0).toAscii() - 'a' + 10;

                str.remove(0,1);

                if(str.size())
                {
                    if(str.at(0) <= '9')
                        byte = (byte<<4) | (str.at(0).toAscii() - '0');
                    else
                        byte = (byte<<4) | (str.at(0).toAscii() - 'a' + 10);

                    str.remove(0,1);
                }

                writeByteArray += byte;
            }
        }
        else
        {
           writeByteArray = str.toAscii();
        }
       write_b = true;
       return true;
    }
    return false;
}

QString * CSerialThread::convertBuffToString()
{
   const char* c=m_buf;
   QString * text = new QString;

   char buf[16];

   if (m_HexOutPut)
   {
       for (int i=0; i<bytesRead; i++)
       {
            if ((m_hexBytes % 16) == 0)
            {
                snprintf(buf, 16, "%08x: ", m_hexBytes);
                (*text)+=buf;
            }
            unsigned int b=*c;
            snprintf(buf, 16, "%02x ", b & 0xff);
            (*text)+=buf;

            m_hexBytes++;
            if ((m_hexBytes % 16)==0)
            {
               (*text)+="\n";
            }
            else if ((m_hexBytes % 8)==0)
            {
               (*text)+="  ";
            }
            c++;
       }
   }
   else
   {
    if(m_DatePrefix)
    {
        (*text) += "(" + QDate::currentDate().toString() + ")";
    }
     if(m_TimePrefix)
    {
         (*text) += "(" + QTime::currentTime().toString() + ")";
    }

    for (int i=0; i<bytesRead; i++)
    {
          if(isprint(*c))
          {
             (*text)+=(*c);
          }
         else if ( (*c=='\n') || (*c=='\r'))
         {         // also print a newline for \r, and print only one newline for \r\n
                (*text)  +=  '\n';
                if((*c == '\r') && (*(c+1)=='\n'))
                {
                   c++;
                    i++;
                }
                /* append date and time here*/
                if(m_DatePrefix)
                {
                    (*text) += "(" + QDate::currentDate().toString() + ")";
                }
                if(m_TimePrefix)
                {
                    (*text) += "(" + QTime::currentTime().toString() + ")";
                }

         }
         else if(m_PrintUnchar)
         {
            unsigned int b=*c;
            snprintf(buf, 16, "(0x%02x)", b & 0xff);
            (*text)+=buf;
         }
        c++;
    }
   }

   return text;
}

void CSerialThread::readData()
{
   bytesRead = m_SerialPort.read(m_buf , SCFRIEND_READ_BUFSIZE);

   if (bytesRead <= 0)
   {
      return;
   }

#ifdef DEBUG
   qDebug() << QTime::currentTime().toString() <<" read "
            << bytesRead << "bytes: " << m_buf;
#endif

    QString * pstr = convertBuffToString();

   if (pstr->isEmpty())
   {
      delete pstr;
      return;
   }
   TrigerCmd(NEW_READ_STRING , pstr);

   if (m_logFile.isOpen())
   {
      m_logFile.write(pstr->toAscii());
   }
   /*   update UI   */
   emit AddStr(pstr);
}

void CSerialThread::setNewOptions()
{
   m_SerialPort.setBaudRate((BaudRateType)baudrate);

   if (dataBits == 7 && (parity=="Mark" || parity == "Space"))
   {
      dataBits = 8;
   }

   DataBitsType dbt;
   switch (dataBits)
   {
        case 5:      dbt = DATA_5;      break;
        case 6:     	dbt = DATA_6;      break;
        case 7: 	dbt = DATA_7;      break;
        case 8:
        default: 	dbt = DATA_8;      break;
   }
   m_SerialPort.setDataBits(dbt);

   ParityType prtp = PAR_NONE;
   if (parity == "Even")
   {
        prtp = PAR_EVEN;
   }
   else if (parity== "Odd")
   {
        prtp = PAR_ODD;
   }
   else if (parity== "Mark")
   {
        prtp = PAR_MARK;
   }
   else if(parity == "Space")
   {
        prtp = PAR_SPACE;
   }
   m_SerialPort.setParity(prtp);

   StopBitsType sbt = STOP_1;
   if (stop=="2")
   {
      sbt = STOP_2;
   }
   else
   {
      sbt = STOP_1;
   }
    m_SerialPort.setStopBits(sbt);

   FlowType tltp = FLOW_OFF;
   if (softwareHandshake)
   {
        tltp = FLOW_XONXOFF;
   }
   else if(hardwareHandshake)
   {
        tltp = FLOW_HARDWARE;
   }
   m_SerialPort.setFlowControl(tltp);

}


bool CSerialThread::enableLogging(bool on, QString path, int lgtype)
{
   if (m_logFile.isOpen())
   {
      m_logFile.close();
   }

   if (on)
   {
       if(lgtype == 0)//auto file name .
       {
           QString name;
           path = path.left( path.lastIndexOf("/")+1);
           name = QDate::currentDate().toString() + "_" +
                   QTime::currentTime().toString() + ".log";
           name.replace(" ","_");
           name.replace(":","_");
           path += name;
       }
      m_logFile.setFileName(path);
      QIODevice::OpenMode mode=QIODevice::ReadWrite;
      if (1 == lgtype)
      {
         mode=mode | QIODevice::Truncate;
      }
      else
      {
         mode=mode | QIODevice::Append;
      }

      if (!m_logFile.open(mode))
      {
          m_errorinfo = tr("Could not open log file %1 for writing.").arg(path);
        return false;
      }
   }

   return true;
}
